////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNScripting/inc/WNInstruction.h"
#include "WNScripting/inc/WNDataType.h"
#include "WNScripting/inc/WNExpression.h"
#include "WNScripting/inc/WNFunctionDecl.h"
#include "WNScripting/inc/WNLValue.h"
#include "WNScripting/inc/WNScriptingConfig.h"
#include "WNScripting/inc/WNLLVMHelpers.h"
#include "WNMemory/inc/WNAllocation.h"

#include <llvm/Intrinsics.h>

using namespace WNScripting;

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                             WN_SIZE_T _endLine,
                             WN_SIZE_T _startChar,
                             WN_SIZE_T _endChar) :
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mReturns(WN_FALSE) {
    mType = eWNInstructionNull;
}

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                WN_SIZE_T _endLine,
                WN_SIZE_T _startChar,
                WN_SIZE_T _endChar,
                WNInstructionList* _instructionList):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mFirstElement.mInstructionList = _instructionList;
    mSecondElement.type = WN_NULL;
    mType = eWNInstructionList;
}
    
WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                WN_SIZE_T _endLine,
                WN_SIZE_T _startChar,
                WN_SIZE_T _endChar,
                WNLValue* _lvalue,
                WNStaticArithmetic _arithType,
                WNExpression* _expression):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mFirstElement.mLvalue = _lvalue;
    mSecondElement.expression = _expression;
    mArithmeticType = _arithType;
    mType = eWNInstructionAssign;
}
   
WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                WN_SIZE_T _endLine,
                WN_SIZE_T _startChar,
                WN_SIZE_T _endChar,
                WNDataType* _type,
                WNIdentifierElement _identifier,
                WNLValue* _lvalue,
                WNExpression* _expression):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mFirstElement.mIdentifier = _identifier;
    mSecondElement.type = _type;
    mThirdElement.expression = _expression;
    mFourthElement.mLvalue = _lvalue;
    mArithmeticType = eWNStaticAssign;
    mType = eWNInstructionDeclaration;
}

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                WN_SIZE_T _endLine,
                WN_SIZE_T _startChar,
                WN_SIZE_T _endChar,
                WNExpression* _expression,
                WN_BOOL _isReturn):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(_isReturn) {
    mType = _isReturn? eWNInstructionReturn: eWNInstructionExpression;
    mSecondElement.expression = _expression;
}

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                             WN_SIZE_T _endLine,
                             WN_SIZE_T _startChar,
                             WN_SIZE_T _endChar,
                             WNExpression* _expression,
                             WNInstruction* _instruction,
                             WNInstructionType _instructionType):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mType = _instructionType;
    mFirstElement.mInstruction = _instruction;
    mSecondElement.expression = _expression;
}

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                             WN_SIZE_T _endLine,
                             WN_SIZE_T _startChar,
                             WN_SIZE_T _endChar,
                             WNExpression* _expression,
                             WNInstruction* _instruction,
                             WNInstruction* _elseInstruction):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mType = eWNInstructionConditionalElse;
    mFirstElement.mInstruction = _instruction;
    mSecondElement.expression = _expression;
    mThirdElement.mInstruction = _elseInstruction;
}

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                             WN_SIZE_T _endLine,
                             WN_SIZE_T _startChar,
                             WN_SIZE_T _endChar,
                             WNInstruction* _initial,
                             WNExpression* _condition,
                             WNInstruction* _loopval,
                             WNInstruction* _loopIntern):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mType = eWNInstructionForLoop;
    mFirstElement.mInstruction = _initial;
    mSecondElement.expression = _condition;
    mThirdElement.mInstruction = _loopval;
    mFourthElement.mInstruction = _loopIntern;
}

WNInstruction::WNInstruction(WN_SIZE_T _startLine,
                             WN_SIZE_T _endLine,
                             WN_SIZE_T _startChar,
                             WN_SIZE_T _endChar,
                             WNLValue* _lvalue,
                             WN_UINT32 _deleteLevels):
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mReturns(WN_FALSE) {
    mFirstElement.mLvalue = _lvalue;
    mSecondElement.number = _deleteLevels;
    mType = eWNInstructionDelete;
}

WNInstruction::~WNInstruction() {
    if (mType == eWNInstructionDeclaration) {
        WN_DELETE(mSecondElement.type);
        WN_DELETE(mThirdElement.expression);
    } else if (mType == eWNInstructionList) {
        if (mFirstElement.mInstructionList != WN_NULL) {
            WN_DELETE(mFirstElement.mInstructionList);   
        }
    } else if (mType == eWNInstructionAssign) {
        WN_DELETE(mSecondElement.expression);
    } else if (mType == eWNInstructionReturn) {
        WN_DELETE(mSecondElement.expression);
    } else if (mType == eWNInstructionExpression) {
        WN_DELETE(mSecondElement.expression);
    } else if (mType == eWNInstructionConditional) {
        WN_DELETE(mSecondElement.expression);
        WN_DELETE(mFirstElement.mInstruction);
    }
    for (WN_UINT32 i = 0; i < mInstructions.size(); ++i) {
        WN_DELETE(mInstructions[i]);
    }
}

WNInstructionList::WNInstructionList(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mInstruction(WN_NULL),
    mInstructionList(WN_NULL) {

}

WNInstructionList::WNInstructionList(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNInstruction* _instruction,
                  WNInstructionList* _instructionList):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mInstruction(_instruction),
    mInstructionList(_instructionList) {
}

llvm::Value* WNInstructionList::OptimizeAndGenerate(WNScriptingEngineData& _scriptingData, WNFunctionDecl* _functionDecl) {
     return(WNParseNode::OptimizeAndGenerate(_scriptingData, _functionDecl));
}

WNInstructionList::~WNInstructionList() {
    if (!mOptimized) {
        if (mInstruction != WN_NULL) {
            WN_DELETE(mInstruction);
        }
        if (mInstructionList != WN_NULL) {
            WN_DELETE(mInstructionList);
        }
    }
}

llvm::Value* WNInstruction::OptimizeAndGenerate(WNScriptingEngineData& _scriptingData, WNFunctionDecl* _functionDecl) {
    llvm::IRBuilder<>* builder = reinterpret_cast<llvm::IRBuilder<>* >(_scriptingData.mBuilder);
    WNParseNode::OptimizeAndGenerate(_scriptingData, _functionDecl);
    WN_BOOL fallThrough = WN_FALSE;
    switch (mType) {
        case eWNInstructionConditional: {
            //TODO: Actually detect dead pieces of code, and do not even code-gen (for now we hope that llvm will take care of dead blocks)
            llvm::Value* val = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);

            if (!val || mSecondElement.expression->GetType() != _scriptingData.mTypeManager->GetType(eWNTypeBool)) {
                mFail = "Error, expression is not of boolean type in if header";
                return(WN_NULL);
            }
            llvm::BasicBlock *IfBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifBlock", _functionDecl->mLLVMFunction);
            llvm::BasicBlock *EndIfBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "endIfBlock");
            builder->CreateCondBr(val, IfBlock, EndIfBlock);
            builder->SetInsertPoint(IfBlock);
            _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);
            mFirstElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
            WN_BOOL returns = mFirstElement.mInstruction->Returns(); 
            _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !returns);

            if (!returns) {
                builder->CreateBr(EndIfBlock);
            }
            _functionDecl->mLLVMFunction->getBasicBlockList().push_back(EndIfBlock);
            builder->SetInsertPoint(EndIfBlock);
            return(val);
            break;
        }
        case eWNInstructionConditionalElse: {
            //TODO: Actually detect dead pieces of code, and do not even code-gen (for now we hope that llvm will take care of dead blocks)

                llvm::Value* val = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);

                if (!val || mSecondElement.expression->GetType() != _scriptingData.mTypeManager->GetType(eWNTypeBool)) {
                    mFail = "Error, expression is not of boolean type in if header";
                    return(WN_NULL);
                }
                llvm::BasicBlock* IfBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifBlock", _functionDecl->mLLVMFunction);
                llvm::BasicBlock* ElseBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "elseBlock");
                llvm::BasicBlock* EndIfBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "endIfBlock");
                builder->CreateCondBr(val, IfBlock, ElseBlock);
               
                builder->SetInsertPoint(IfBlock);
                _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);
                mFirstElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                WN_BOOL mFirstReturns = mFirstElement.mInstruction->Returns();                
                _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !mFirstReturns);

                if (!mFirstReturns) {
                    builder->CreateBr(EndIfBlock);
                }

                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(ElseBlock);
                builder->SetInsertPoint(ElseBlock);
                _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);
                mThirdElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                WN_BOOL mSecondReturns = mThirdElement.mInstruction->Returns();                
                _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !mSecondReturns);

                if (!mSecondReturns) {
                    builder->CreateBr(EndIfBlock);
                }
                mReturns = (mFirstReturns && mSecondReturns);
                if (!mReturns) {
                    _functionDecl->mLLVMFunction->getBasicBlockList().push_back(EndIfBlock);
                    builder->SetInsertPoint(EndIfBlock);
                }
                return(val);
                break;
            }
        case eWNInstructionWhileLoop: {
                llvm::BasicBlock* whileHeader = llvm::BasicBlock::Create(llvm::getGlobalContext(), "whileHeader", _functionDecl->mLLVMFunction);
                llvm::BasicBlock* whileBody = llvm::BasicBlock::Create(llvm::getGlobalContext(), "whileBody");
                llvm::BasicBlock* whileEnd = llvm::BasicBlock::Create(llvm::getGlobalContext(), "whileEnd");
                builder->CreateBr(whileHeader);
                builder->SetInsertPoint(whileHeader);
                _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);
                llvm::Value* val = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                if (!val || mSecondElement.expression->GetType() != _scriptingData.mTypeManager->GetType(eWNTypeBool)) {
                    mFail = "Error, expression is not of boolean type in if header";
                    return(WN_NULL);
                }
                builder->CreateCondBr(val, whileBody, whileEnd);

                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(whileBody);
                builder->SetInsertPoint(whileBody);
                mFirstElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                WN_BOOL returns = mFirstElement.mInstruction->Returns();
                _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !returns);
                if (!returns) {
                    builder->CreateBr(whileHeader);
                }

                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(whileEnd);
                builder->SetInsertPoint(whileEnd);
               
                return(val);
            }
            break;
        case eWNInstructionDoLoop:
            {
                llvm::BasicBlock* doBody = llvm::BasicBlock::Create(llvm::getGlobalContext(), "doBody", _functionDecl->mLLVMFunction);
                llvm::BasicBlock* doEnd = llvm::BasicBlock::Create(llvm::getGlobalContext(), "doEnd");

                builder->SetInsertPoint(doBody);
                _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);
                mFirstElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                WN_BOOL returns = mFirstElement.mInstruction->Returns();
                llvm::Value* val = WN_NULL;
                _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !returns);
                if (!returns) {
                    llvm::Value* val = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                    if (!val || mSecondElement.expression->GetType() != _scriptingData.mTypeManager->GetType(eWNTypeBool)) {
                        mFail = "Error, expression is not of boolean type in do condition header";
                        return(WN_NULL);
                    }
                    builder->CreateCondBr(val, doBody, doEnd);
                }
                mReturns = returns;
                

                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(doEnd);
                builder->SetInsertPoint(doEnd);

                return(val);
            }
            break;
        case eWNInstructionForLoop: {
                _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);

                llvm::Value * val = mFirstElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                llvm::BasicBlock* doHeader = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ForHeader", _functionDecl->mLLVMFunction);
                llvm::BasicBlock* doBody = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ForBody");
                llvm::BasicBlock* endDo = llvm::BasicBlock::Create(llvm::getGlobalContext(), "EndFor");
                
                builder->CreateBr(doHeader);
                builder->SetInsertPoint(doHeader);
                llvm::Value* condVal = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                if (!condVal || mSecondElement.expression->GetType() != _scriptingData.mTypeManager->GetType(eWNTypeBool)) {
                    mFail = "Error, expression is not of boolean type in if header";
                    return(WN_NULL);
                }
                builder->CreateCondBr(condVal, doBody, endDo);
                
                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(doBody);
                builder->SetInsertPoint(doBody);
                mFourthElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                mThirdElement.mInstruction->OptimizeAndGenerate(_scriptingData, _functionDecl);
                WN_BOOL returns = mThirdElement.mInstruction->Returns();
                _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !returns);
                if (!returns) {
                    builder->CreateBr(doHeader);
                }

                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(endDo);
                builder->SetInsertPoint(endDo);
                
                return(val);
            }
            break;
        case eWNInstructionList: {
                WNInstructionList* l = mFirstElement.mInstructionList;
                mFirstElement.mInstructionList = WN_NULL;
                while(l != WN_NULL){
                    WNInstruction* inst = l->AppropriateInstruction();
                    if (inst != WN_NULL) {
                        mInstructions.push_front(inst);
                    }
                    WNInstructionList* l2 = l->AppropriateInstructionList();
                    WN_DELETE(l);
                    l = l2;
                }
                _scriptingData.mScopedVariableList->PushScopeBlock(_scriptingData);
                llvm::Value * val = WN_NULL;
                WN_BOOL returns = WN_FALSE;
                for(WN_UINT32 i = 0; i < mInstructions.size(); ++i) {
#ifndef WN_SCRIPTING_ALLOW_CODE_AFTER_RETURN
                    if (returns) {
                        mError = "Code after RETURN";
                        return(WN_NULL);
                    }
#else 
                    if (returns) {
                        mWarning = "Code after RETURN";
                        continue;
                    }
#endif
                    val = mInstructions[i]->OptimizeAndGenerate(_scriptingData, _functionDecl);
                    returns = mInstructions[i]->Returns();
                }
                mReturns = returns;
                _scriptingData.mScopedVariableList->PopScopeBlock(_scriptingData, !returns);
                return(val);
        }
        break;
        case eWNInstructionDeclaration: {
            llvm::Value* allocVal = mSecondElement.type->OptimizeAndGenerate(_scriptingData, _functionDecl);
            if (!mSecondElement.type->GetType()) {
                mFail = "Invalid type detected";
                return(WN_NULL);
            }
            WNScriptVariable var = {mSecondElement.type->GetType(), mFirstElement.mIdentifier, allocVal};
            var.mLlvmAlloca = WNLLVMHelpCreateAlloca(mSecondElement.type, _functionDecl, mFirstElement.mIdentifier, _scriptingData);
            _scriptingData.mScopedVariableList->PushVariable(var);
            fallThrough = WN_TRUE;
        }
        case eWNInstructionAssign: {
            
            llvm::Value* val = WN_NULL;
            WNExpression* expr = WN_NULL;
            llvm::Value* var = WN_NULL;
            WNLValue* lval = WN_NULL;
            if (fallThrough) {
                val = mThirdElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                expr = mThirdElement.expression;
                var = mFourthElement.mLvalue->OptimizeAndGenerate(_scriptingData, _functionDecl);
                lval = mFourthElement.mLvalue;
            } else {
                val = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                expr = mSecondElement.expression;
                var = mFirstElement.mLvalue->OptimizeAndGenerate(_scriptingData, _functionDecl);
                lval = mFirstElement.mLvalue;
            }

            if (!WNIsValid(var)) {
                mFail = "Invalid Variable Assignment";
                return(WN_NULL);
            }   
            WNTypeElement type;
            WNCastAllowance castAllow = _scriptingData.mTypeManager->GetDynamicArithmeticVerification(lval->GetOutType(), expr->GetType())(eWNStaticAssign, lval->GetOutType(), expr->GetType(), type);
            if (castAllow == eWNCastFail) {
                mFail = "Cannot cast expression to variable type";
                return(WN_NULL);
            } else if (castAllow == eWNCastInvalidArithmetic) {
                mFail = "Error invalid arithmetic operator";
                return(WN_NULL);
            } else if (castAllow == eWNCastLossPrecision) {
                mWarning = "The cast can proceed, but there will be loss of precision";
            }

            WNGenerateLLVMCast cast = _scriptingData.mTypeManager->GetLLVMCastGenerator(lval->GetOutType(), expr->GetType());
            llvm::Value* castedVal = cast(val, *builder);
            
            WNCreateStore store = _scriptingData.mTypeManager->GetLLVMStoreInst(lval->GetOutType());

            return(store(_scriptingData, castedVal, var, fallThrough,  mArithmeticType));
        }
        break;
        case eWNInstructionReturn: {
                llvm::Value* returnVal = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                if (returnVal == WN_NULL) {
                    return(WN_NULL);
                }
                
                llvm::Value* castVal = WN_NULL;
                if(_functionDecl->mReturnType == mSecondElement.expression->GetType()) {
                    castVal = returnVal;
                } else {
                    WNGenerateLLVMCast cast = _scriptingData.mTypeManager->GetLLVMCastGenerator(_functionDecl->mReturnType, mSecondElement.expression->GetType());

                    castVal = cast(returnVal, *builder);
                    if (castVal == WN_NULL) {
                        mFail = "Error creating return value. Cannot cast types";
                        return(WN_NULL);
                    }
                }
                mReturns = WN_TRUE;
                if(_functionDecl->mStackReturn) {
                    WNFunction* func = _scriptingData.mFunctionManager->GetTemporaryStackFunction();
                    WNGetCopySize copySize = _scriptingData.mTypeManager->GetLLVMCopySize(_functionDecl->mReturnType);
                    llvm::Value* v = copySize(_scriptingData, castVal);
                    std::vector<llvm::Value*> params;
                    params.push_back(v);
                    llvm::Value* copyLoc = builder->CreateCall(func->mLlvmFunction, params);
                    llvm::Type* llvmChar = _scriptingData.mTypeManager->GetLLVMType(_scriptingData.mTypeManager->GetType(eWNTypeChar));
                    std::vector<llvm::Type*> memcpyTypes;

                    memcpyTypes.push_back(llvm::PointerType::getUnqual(llvmChar));
                    memcpyTypes.push_back(llvm::PointerType::getUnqual(llvmChar));
                    memcpyTypes.push_back(llvm::IntegerType::getInt32Ty(llvm::getGlobalContext()));

                    llvm::Function* fnc = llvm::Intrinsic::getDeclaration(_scriptingData.mModule, llvm::Intrinsic::memcpy, memcpyTypes);
                    std::vector<llvm::Value*> args;
                    castVal = builder->CreatePointerCast(castVal, llvm::PointerType::getUnqual(llvmChar));
                    copyLoc = builder->CreatePointerCast(copyLoc, llvm::PointerType::getUnqual(llvmChar));

                    args.push_back(copyLoc);
                    args.push_back(castVal);
                    args.push_back(v);
                    args.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0));
                    args.push_back(llvm::ConstantInt::get(llvm::Type::getInt1Ty(llvm::getGlobalContext()), 0));
                    builder->CreateCall(fnc, args, "");
                    return(builder->CreateRet(v));
                } else {
                     return(builder->CreateRet(castVal));
                }
            }
            break;
        case eWNInstructionExpression: {
                llvm::Value* expr = mSecondElement.expression->OptimizeAndGenerate(_scriptingData, _functionDecl);
                return(expr);
            }
        case eWNInstructionDelete: {
                llvm::Value* lVal = mFirstElement.mLvalue->OptimizeAndGenerate(_scriptingData, _functionDecl);
                
                if(!_scriptingData.mTypeManager->IsReferenceType(mFirstElement.mLvalue->GetOutType())) {
                    return(WN_NULL);
                }
                lVal = builder->CreateLoad(lVal);
                WNLLVMCreateFree(_scriptingData, _functionDecl, lVal, mFirstElement.mLvalue->GetOutType(), mSecondElement.number);
                return(lVal);
            }
            break;
        default:
            break;
    };
    return(WN_NULL);
}

WN_BOOL WNInstruction::ReportErrors() {
    WN_BOOL retVal = WNParseNode::ReportErrors();
    switch(mType) {
    case eWNInstructionAssign:
        retVal &= mSecondElement.expression->ReportErrors();
        break;
    case eWNInstructionConditionalElse:
        retVal &= mFirstElement.mInstruction->ReportErrors();
        retVal &= mSecondElement.expression->ReportErrors();
        retVal &= mThirdElement.mInstruction->ReportErrors();
        break;

    case eWNInstructionDeclaration:
        retVal &= mSecondElement.type->ReportErrors();
        retVal &= mThirdElement.expression->ReportErrors();
        break;
    case eWNInstructionForLoop:
        retVal &= mFirstElement.mInstruction->ReportErrors();
        retVal &= mSecondElement.expression->ReportErrors();
        retVal &= mThirdElement.mInstruction->ReportErrors();
        retVal &= mFourthElement.mInstruction->ReportErrors();
        break;
    case eWNInstructionList:
        for(WN_UINT32 i = 0; i < mInstructions.size(); ++i) {
            retVal &= mInstructions[i]->ReportErrors();
        }
        break;
    case eWNInstructionNull:
        break;
    case eWNInstructionReturn:
        retVal &= mSecondElement.expression->ReportErrors();
        break;
    case eWNInstructionDoLoop:
    case eWNInstructionWhileLoop:
    case eWNInstructionConditional:
        retVal &= mFirstElement.mInstruction->ReportErrors();
        retVal &= mSecondElement.expression->ReportErrors();
        break;
    }
    return retVal;
}

WN_BOOL WNInstructionList::ReportErrors() {
    WN_BOOL retVal = WNParseNode::ReportErrors();
    return(retVal);
}

WN_BOOL WNInstruction::Returns() {
    return(mReturns);
}

WN_BOOL WNInstructionList::Returns() {
    return(mReturns);
}
